/*
SubspaceMobile - A subspace/continuum client for mobile phones
Copyright (C) 2010 Kingsley Masters
Email: kshade2001 at users.sourceforge.net
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
REVISIONS:
*/
package com.subspace.network;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.AsynchronousCloseException;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
import java.nio.channels.NotYetConnectedException;
import android.util.Log;
public abstract class Network implements Runnable {
private static final boolean LOG_RAW_PACKETS = false;
protected static final String TAG = "Subspace";
private static final int MAX_UDP_PACKETSIZE = 512;
protected INetworkCallback callback;
private Thread networkThread;
private DatagramChannel channel;
private boolean isRunning;
private boolean isConnected = false;
private int packetOutCount = 0;
private int packetInCount = 0;
private long BytesOut = 0;
private long BytesIn = 0;
public Network(INetworkCallback callback) {
setCallback(callback);
}
public final void setCallback(INetworkCallback callback) {
this.callback = callback;
}
protected final void Connect(String host, int port) throws IOException {
isConnected = false;
InetSocketAddress remoteHostaddress = new InetSocketAddress(host, port);
channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.connect(remoteHostaddress);
// woo we are connected
networkThread = new Thread(this);
isRunning = true;
networkThread.start();
isConnected = true;
}
public final void run() {
ByteBuffer buffer = ByteBuffer.allocateDirect(MAX_UDP_PACKETSIZE);
buffer.order(ByteOrder.LITTLE_ENDIAN); // subspace uses little endian
while (isRunning) {
try {
buffer.clear();
int lengthOfData = channel.read(buffer);
// ignore random empty packets
if (lengthOfData > 0) {
// increment packet count
BytesIn += lengthOfData;
packetInCount++;
// flip buffer
buffer.flip();
// verbose
if (LOG_RAW_PACKETS) {
Log.v(TAG, "R:" + Util.ToHex(buffer));
}
// send call back
callback.Recv(buffer, true);
}
} catch (AsynchronousCloseException ioe) {
Log.v(TAG, "Timeout exceeded, interrupted");
} catch (ClosedChannelException cce) {
Log.v(TAG, "Connection Closed by host, no response");
} catch (PortUnreachableException pue) {
Log.v(TAG, "Unable to connect, no response");
} catch (NotYetConnectedException pue) {
Log.v(TAG, "Unable to connect, no response");
} catch (Exception ioe) {
Log.e(TAG, Log.getStackTraceString(ioe));
}
}
}
protected final void Send(ByteBuffer buffer) throws IOException {
// wait for connection first
while (!isConnected) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
Log.e(TAG, Log.getStackTraceString(ie));
break;
}
}
if (buffer != null) {
// rewind
buffer.rewind();
// verbose
if (LOG_RAW_PACKETS) {
Log.v(TAG, "S:" + Util.ToHex(buffer));
}
// write
int writenBytes = channel.write(buffer);
// stats
BytesOut += writenBytes;
packetOutCount++;
} else {
Log.e(TAG, "attempted to send a null buffer");
}
}
protected final void Close() throws IOException {
isRunning = false;
channel.disconnect();
channel.close();
if (this.networkThread.isAlive()) {
this.networkThread.interrupt();
}
}
// accessers
public final int getSentDatagramCount() {
return packetOutCount;
}
public final int getRecvDatagramCount() {
return packetInCount;
}
}